//======================================================================================
/** \file virtest.cpp
 *    This file contains a program to test the speeds at which virtual methods, non
 *    virtual methods, and ordinary functions can be called in AVR-GCC. It also shows
 *    the syntax for declaring and using such methods, of course. 
 *
 *  Revisions
 *    \li 02-03-2008 JRR Original file
 *    \li 03-04-2008 JRR Added multiple inheritance tests
 *    \li 05-06-2008 JRR Changed to use new serial port classes
 *    \li 01-29-2009 JRR Changed again to use even newer serial port classes
 */
//======================================================================================

											// System headers included with < >
#include <stdlib.h>							// Standard C library
#include <avr/io.h>							// Input-output ports, special registers
#include <avr/interrupt.h>					// Interrupt handling functions

											// User written headers included with " "
#include "rs232int.h"						// Serial port header
#include "stl_timer.h"						// Microsecond-resolution timer
#include "virtest_classes.h"				// Classes containing virtual methods


/** This is the number of times we run each method. If we're just testing which method
 *  runs (parent or child) during each call, set it to 1; if we're doing a speed test,
 *  set it to something like 10000.
 */
#define TEST_RUNS			1				// Number of times to run each method


//--------------------------------------------------------------------------------------
/** The main function is the "entry point" of every C program, the one which runs first
 *  (after standard setup code has finished). For mechatronics programs, main() runs an
 *  infinite loop and never exits. 
 */

int main ()
{
	char time_string[16];					// Character buffer holds printable time
	char input_char;						// A character typed by the user
	volatile unsigned int loops;			// Counter for looping function calls
	time_stamp begin_time;					// Time stamp when function runs begin
	time_stamp end_time;					// Time stamp for computing intervals
	time_stamp no_time;						// Time to run no functions at all
	task_timer the_timer;					// Real-time counter

	sei ();									// Enable interrupts for timer, rs232

	// Create a serial port object. The time will be printed to this port, which
	// should be hooked up to a dumb terminal program like minicom on a PC
	rs232 the_serial_port (9600, 1);

	// Print a greeting message. This is almost always a good thing because it lets 
	// the user know that the program is actually running
	the_serial_port << clrscr << "ME405: Inherited Method Test" << endl;

	// Create parent a child class object, and pointers to that object
	child1 the_kid_1 (&the_serial_port);
	mom* p_mom1 = &the_kid_1;
	child1* p_child1 = &the_kid_1;

	// Repeat the process, but with multiple inheritance
	child2 the_kid_2 (&the_serial_port);
	mom* p_mom2 = &the_kid_2;
	child2* p_child2 = &the_kid_2;

	// Don't run any function at all -- get a baseline timing delay
	the_serial_port << "Running no function at all...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++);
	no_time = the_timer.get_time_now ();
	no_time -= begin_time;
	the_serial_port << no_time << endl;

	// Run the ordinary function
	the_serial_port << "Running ordinary C function...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		just_function ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the non-virtual overridden method with a parent class pointer
	the_serial_port << "Ordinary C++ method with parent pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_mom1->omethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the non-virtual overridden method with a child class pointer
	the_serial_port << "Ordinary C++ method with child pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_child1->omethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the virtual method with a pointer to the child class
	the_serial_port << "Virtual C++ method with parent pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_mom1->vmethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the virtual method with a pointer to the child class
	the_serial_port << "Virtual C++ method with child pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_child1->vmethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the non-virtual overridden method with a parent class pointer
	the_serial_port << "Double-inheritance C++ method with parent pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_mom2->omethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the non-virtual overridden method with a child class pointer
	the_serial_port << "Double-inheritance C++ method with child pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_child2->omethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the virtual method with a pointer to the child class
	the_serial_port << "Double-inheritance virtual C++ method with parent pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_mom2->vmethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	// Run the virtual method with a pointer to the child class
	the_serial_port << "Double-inheritance virtual C++ method with child pointer...";
	begin_time = the_timer.get_time_now ();
	for (loops = 0; loops < TEST_RUNS; loops++)
		p_child2->vmethod ();
	end_time = the_timer.get_time_now ();
	end_time -= (begin_time + no_time);
	the_serial_port << end_time << endl;

	the_serial_port << "Test complete." << endl;

	// Run the main scheduling loop, in which the action to run is done repeatedly.
	// In the future, we'll run tasks here; for now, just do things in a simple loop
	while (true)
	{
		// Check if the user has typed something. If so, either clear the counter to
		// a reading of zero time or write a carriage return and linefeed
		if (the_serial_port.check_for_char ())
		{
			input_char = the_serial_port.getchar ();
			if (input_char == 'c')
				the_serial_port << endl;
		}
	}

	return (0);
}
